package javaapplication8.Make;

// $Source: /h/u1/szamosi/repo/b07/examples/parse/ParseStateMachine.java,v $
// $Date: 2007/06/24 00:25:12 $
// $Revision: 1.1.1.1 $
// Authors: G. Wilson; J. Clarke
//
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

class ParseStateMachine {

    // Legal states
    protected static final String STATE_INITIAL   = "initial";
    protected static final String STATE_HEAD_ONLY = "head only";
    protected static final String STATE_HEAD_BODY = "head and body";

    //------------------------------------------------------------

    // Parse Makefile rules from input stream
    public List parse(BufferedReader input) {

        // Initialize
        fLineNum = 0;
        fRules = new ArrayList();
        String state = STATE_INITIAL;

        // Read input a line at a time
        String line;
        while ((line = readLine(input)) != null) {

            // Initial empty state goes to head-only state.
            if (state.equals(STATE_INITIAL)) {
                state = handleInitialState(line);
            }

            // Head-only state goes to head-and-body state.
            else if (state.equals(STATE_HEAD_ONLY)) {
                state = handleHeadOnlyState(line);
            }

            // Head-and-body state can go either way.
            else if (state.equals(STATE_HEAD_BODY)) {
                state = handleHeadBodyState(line);
            }

            else {
                fail("Illegal state '" + state + "'");
            }
        }

        // Finish last rule and report.
        if (state.equals(STATE_HEAD_ONLY)) {
            fail("Last rule has head but no body");
        }
        finishRule();
        return fRules;
    }

    // Main driver
    public static void main(String[] args) {
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        ParseStateMachine parser = new ParseStateMachine();
        List rules = parser.parse(input);
        Iterator ir = rules.iterator();
        while (ir.hasNext()) {
            System.out.println(ir.next());
        }
    }

    //------------------------------------------------------------

    // How to handle input in initial state.
    protected String handleInitialState(String line) {
        assert fCurrentRule == null : "Current rule not null in initial state";
        if (!isHeadLine(line)) {
            fail("Non-head line at start of input");
        }
        startRule(line);
        return STATE_HEAD_ONLY;
    }

    // How to handle input in head-only state.
    protected String handleHeadOnlyState(String line) {
        assert fCurrentRule != null : "Current rule null in head-only state";
        if (!isActionLine(line)) {
            fail("Non-body line after head line");
        }
        fCurrentRule.addAction(line.trim());
        return STATE_HEAD_BODY;
    }

    // How to handle input in head-and-body state.
    protected String handleHeadBodyState(String line) {
        assert fCurrentRule != null : "Current rule null in head-and-body state";
        if (isHeadLine(line)) {
            finishRule();
            startRule(line);
            return STATE_HEAD_ONLY;
        }
        if (!isActionLine(line)) {
            fail("Unrecognized line in head-and-body state");
        }
        fCurrentRule.addAction(line.trim());
        return STATE_HEAD_BODY;
    }

    // Is this line the start of a rule?
    protected boolean isHeadLine(String line) {
        return line.charAt(0) != '\t';
    }

    // Is this line an action?
    protected boolean isActionLine(String line) {
        return line.charAt(0) == '\t';
    }

    // Start a new rule
    protected void startRule(String line) {
        fCurrentRule = new Rule();
        try {
            // Get target
            StringTokenizer st = new StringTokenizer(line, ":");
            String target = st.nextToken().trim();
            fCurrentRule.setTarget(target);

            // Get pre-requisites
            String allPrereqs = st.nextToken().trim();
            StringTokenizer pt = new StringTokenizer(allPrereqs);
            while (pt.hasMoreTokens()) {
                fCurrentRule.addPrereq(pt.nextToken());
            }
        }
        catch (NoSuchElementException e) {
            fail("Unable to find tokens on line");
        }
    }

    // Finish the current rule
    protected void finishRule() {
        fRules.add(fCurrentRule);
        fCurrentRule = null;
    }

    // Report error and exit
    protected void fail(String message) {
        message += " (line " + fLineNum + ")";
        System.err.println(message);
        System.exit(0);
    }

    // Read an interesting (non-blank, non-comment) line
    protected String readLine(BufferedReader input) {
        try {
            String line;
            while ((line = input.readLine()) != null) {
                fLineNum += 1;
                String tmp = line.trim();
                if ((tmp.length() > 0) && (tmp.charAt(0) != '#')) {
                    return line;
                }
            }
        }
        catch (IOException e) {
            System.out.println(e);
        }

        // No valid line found
        return null;
    }

    //------------------------------------------------------------

    protected List      fRules;         // rules found in parsing
    protected Rule      fCurrentRule;   // rule currently being built
    protected int       fLineNum;       // what line in input?
}
